// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Linq;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Sdk;

namespace TestValidationAspects
{
	[TestFixture]
	public class TestTypeValidationRegistry
	{
		[SetUp]
		public void SetUp()
		{
			TestHelpers.ResetFramework();
		}

		[Test]
		public void Test_FactoryDiscoveryFunc_for_types_with_ValidationMethods()
		{
			Dummy.ValidateWasCalled = false;
			typeof(Dummy).ResolveValidator().Invoke(new Dummy(), null);
			Assert.IsTrue(Dummy.ValidateWasCalled);
		}

		[Test]
		public void Test_FactoryDiscoveryFunc_for_types_with_no_ValidationMethods()
		{
			Assert.IsNull(typeof(NoValidationMethods).ResolveValidator());
		}

		[Test]
		public void Test_FactoryDiscoveryFunc_for_types_with_validator_applied_by_attribute()
		{
			Assert.IsNull(Dummy2Factory.Value);
			Assert.IsNull(Dummy2Factory.Context);

			Dummy2 value = new Dummy2();
			IValidationContext context = new MockValidationContext();
			typeof(Dummy2).ResolveValidator().Invoke(value, context);

			Assert.AreSame(value, Dummy2Factory.Value);
			Assert.AreSame(context, Dummy2Factory.Context);
		}

		#region Test RevertValidation
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_RevertValidation_throws_for_null_target_param()
		{
			TypeValidationRegistry.RevertValidation(null);
		}

		[Test]
		public void Test_RevertValidation()
		{
			// test for coverage
			GetType().RevertValidation();
		}
		#endregion

		#region Test ReplaceValidation
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ReplaceValidation_throws_for_null_target_param()
		{
			TypeValidationRegistry.ReplaceValidation(null, Enumerable.Empty<IValidatorFactory>());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ReplaceValidation_throws_for_null_factories_param()
		{
			GetType().ReplaceValidation(null);
		}

		[Test]
		public void Test_ReplaceValidation()
		{
			MockValidatorFactory<Dummy> factory = new MockValidatorFactory<Dummy>();
			typeof(Dummy).ReplaceValidation(new[] {factory});

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			typeof(Dummy).ResolveValidator().Invoke(value, context);

			factory.TestState(value, context);
		}
		#endregion

		#region Test ReplaceValidationT
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ReplaceValidationT_throws_for_null_target_param()
		{
			TypeValidationRegistry.ReplaceValidation<object>(null, (v, c) => { });
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ReplaceValidationT_throws_for_null_factories_param()
		{
			GetType().ReplaceValidation<object>(null);
		}

		[Test]
		public void Test_ReplaceValidationT()
		{
			Dummy valueFromValidator = new Dummy();
			IValidationContext contextFromValidator = null;
			typeof(Dummy).ReplaceValidation<Dummy>((v, c) =>
			                                       {
			                                       	valueFromValidator = v;
			                                       	contextFromValidator = c;
			                                       });

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			typeof(Dummy).ResolveValidator().Invoke(value, context);

			Assert.AreSame(value, valueFromValidator);
			Assert.AreSame(context, contextFromValidator);
		}
		#endregion

		#region Test AddValidation
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_AddValidation_throws_for_null_target_param()
		{
			TypeValidationRegistry.AddValidation(null, Enumerable.Empty<IValidatorFactory>());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_AddValidation_throws_for_null_factories_param()
		{
			GetType().AddValidation(null);
		}

		[Test]
		public void Test_AddValidation()
		{
			MockValidatorFactory<Dummy> factory = new MockValidatorFactory<Dummy>();
			typeof(Dummy).AddValidation(new[] {factory});

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			typeof(Dummy).ResolveValidator().Invoke(value, context);

			factory.TestState(value, context);
		}
		#endregion

		#region Test AddValidationT
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_AddValidationT_throws_for_null_target_param()
		{
			TypeValidationRegistry.AddValidation<object>(null, (v, c) => { });
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_AddValidationT_throws_for_null_action_param()
		{
			GetType().AddValidation<object>(null);
		}

		[Test]
		public void Test_AddValidationT()
		{
			Dummy valueFromValidator = new Dummy();
			IValidationContext contextFromValidator = null;
			typeof(Dummy).AddValidation<Dummy>((v, c) =>
			                                   {
			                                   	valueFromValidator = v;
			                                   	contextFromValidator = c;
			                                   });

			Dummy value = new Dummy();
			IValidationContext context = new MockValidationContext();
			typeof(Dummy).ResolveValidator().Invoke(value, context);

			Assert.AreSame(value, valueFromValidator);
			Assert.AreSame(context, contextFromValidator);
		}
		#endregion

		#region Test ResolveValidator
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ResolveValidator_throws_for_null_target_param()
		{
			TypeValidationRegistry.ResolveValidator(null);
		}

		[Test]
		public void Test_ResolveValidator_returns_null_for_no_validators()
		{
			typeof(Dummy).ReplaceValidation(Enumerable.Empty<IValidatorFactory>());
			Assert.IsNull(typeof(Dummy).ResolveValidator());
		}
		#endregion

		#region Mocks
		public class Dummy
		{
			public static bool ValidateWasCalled { get; set; }

			[ValidationMethod]
			public static void Validate(Dummy dummy)
			{
				ValidateWasCalled = true;
			}
		}

		public class NoValidationMethods {}

		public interface IService {}

		internal class Service : IService {}

		[Dummy2Validator]
		public class Dummy2 {}

		[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
		public sealed class Dummy2ValidatorAttribute : ValidatorAttribute
		{
			#region Overrides of ValidatorAttribute
			public override IValidatorFactory Factory
			{
				get { return new Dummy2Factory(); }
			}
			#endregion
		}

		public class Dummy2Factory : ValidatorFactoryBase<Dummy2>
		{
			public static Dummy2 Value { get; set; }
			public static IValidationContext Context { get; set; }

			#region Overrides of ValidatorFactoryBase<Dummy2>
			protected override Action<Dummy2, IValidationContext> ValidateAction
			{
				get
				{
					return (v, c) =>
					       {
					       	Value = v;
					       	Context = c;
					       };
				}
			}
			#endregion
		}
		#endregion
	}
}